iT邦幫忙

2022 iThome 鐵人賽

DAY 17
0
自我挑戰組

30天學習flutter系列 第 17

17.flutter的狀態管理(五)

  • 分享至 

  • xImage
  •  

狀態管理:Riverpod

Riverpod是來自與Provider同一個開發者Remi,Riverpod是作者為了解決Provider一些的問題而開發出來的

我們都知道由於Provider是InheritedWidget的封裝,所以在讀取狀態時需要BuildContext。在開發時很容易在不理解InheritedWidget和BuildContext時,跨頁面獲取狀態經常會ProviderNotFoundException。

  • Riverpod不再依賴Flutter,使pattern獨立於flutter,也就是繞過InheritedWidget,來解決在BuildContext上常發生的問題。
  • 並且在編譯時發現Error而不是在運行時捕獲編程錯誤(runtime exception)
  • 並且移除listening/combining對象的嵌套
  • 能有多個相同類型的provider,provider可以是私有的

基本使用

安裝依賴

riverpod提供了三種方式來使用
https://ithelp.ithome.com.tw/upload/images/20221002/20108931INh8ftK4qe.png

我們這邊使用flutter + flutter_hooks的形式

pubspec.yaml

  flutter_hooks: ^0.18.0
  hooks_riverpod: ^2.0.0-dev.9

https://ithelp.ithome.com.tw/upload/images/20221002/20108931ooKr1FFoHq.png

添加ProviderScope

在widget tree的根部添加ProviderScope,用來儲存各種provider

這裡我們加入ProviderScope,並移除Provider
main.dart

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:todo/states/todo_state.dart';
import 'package:todo/ui/screens/nav_screen.dart';

void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: NavScreen(),
      debugShowCheckedModeBanner: false,
    );
  }
}

將我們的state修改,並加入StateNotifier和Provider

todo_state.dart

import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:todo/models/todo_model.dart';
import 'package:todo/states/Itodo_state.dart';

class TodoState extends StateNotifier<List<TodoModel>> implements ITodoState {
  TodoState() : super([]);

  @override
  List<TodoModel> getTodos() {
    // TODO: implement getTodos
    return state;
  }

  @override
  void addTodo(TodoModel todo) {
    // TODO: implement addTodo
    state = [
      ...state,
      todo,
    ];
  }
}

final todosProvider = StateNotifierProvider<TodoState, List<TodoModel>>((ref) {
  return TodoState();
});

lib\ui\widgets\todo_list_widget.dart:

import 'package:flutter/cupertino.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:todo/models/todo_model.dart';
import 'package:todo/states/todo_state.dart';
import 'package:todo/ui/widgets/todo_item_widget.dart';

class TodoList extends ConsumerWidget {
  const TodoList({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    List<TodoModel> todos = ref.watch(todosProvider);

    return Column(
      children: todos
          .map(
            (e) => TodoItem(title: e.title, done: e.done),
          )
          .toList(),
    );
  }
}

lib\ui\screens\home_screen.dart

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:todo/models/todo_model.dart';
import 'package:todo/states/todo_state.dart';
import 'package:todo/ui/widgets/todo_list_widget.dart';

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final TrackingScrollController _trackingScrollController =
      TrackingScrollController();

  @override
  Widget build(BuildContext context) {
    // final _todoState = Provider.of<TodoState>(context);

    TodoModel testTodo = TodoModel(
        title: "Just Test", done: false, date: DateTime.now().toString());

    return CustomScrollView(
      controller: _trackingScrollController,
      slivers: [
        SliverAppBar(
          title: Text('Home Screen'),
        ),
        SliverToBoxAdapter(
          child: TodoList(),
        ),
        SliverToBoxAdapter(child: AddTodo(testTodo)),
      ],
    );
  }
}

class AddTodo extends ConsumerWidget {
  final TodoModel todo;
  const AddTodo(this.todo, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return ElevatedButton(
      child: Text('Add todo test'),
      onPressed: () {
        ref.read(todosProvider.notifier).addTodo(todo);
      },
    );
  }
}

測試Riverpod

https://ithelp.ithome.com.tw/upload/images/20221002/20108931e1O79x68bt.png

https://ithelp.ithome.com.tw/upload/images/20221002/20108931FWuFzcgRDi.png

今天簡單的介紹了Riverpod如何使用後,我們大致上了解如何使用狀態管理了,我們如果將裝置Restart會發現Todos會消失,我們接下來的目標是將我們的狀態給儲存起來,接下來會介紹如何持久的儲存我們的狀態


上一篇
16.flutter的狀態管理(四)
下一篇
18.flutter的持久化存儲(一)
系列文
30天學習flutter30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言